//
// Created by breaktcn on 2017/6/13.
//
#include "my_muxing.h"
#define CAMERA "avfoundation"
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)

int thread_exit = 0;

int muxing_file_to_file(char *filename){
    //input
    AVInputFormat *mp4_input_format;
    AVFormatContext *mp4_format_cxt = NULL;
    AVStream *mp4_video_stream,*mp4_audio_stream;
    AVCodecContext *mp4_video_codec_cxt = NULL,*mp4_audio_codec_cxt = NULL,*encode_video_codec_cxt,*encode_audio_codec_cxt;
    AVCodec *mp4_video_codec = NULL,*mp4_audio_codec = NULL,*encode_video_codec,*encode_audio_codec;
    AVCodecParameters *mp4_video_codec_parameters = NULL ,*mp4_audio_codec_parameters= NULL;
    AVPacket mp4_pkt,*picture_pkt;
    AVFrame *screen_frame,*screen_frame_YUV;
    AVDictionary *dictionary = NULL;
    int audio_index = 0 ,video_index = 0;
    int frameFinished = 0;
    int ret = 0;

    //output
    AVOutputFormat *file_out_format;
    AVFormatContext *file_format_cxt = NULL;
    AVStream *file_audio_stream,*file_video_stream;
    AVPacket pkt;


    av_register_all();

    mp4_format_cxt = avformat_alloc_context();
    avformat_open_input(&mp4_format_cxt,filename,NULL,NULL);
    avformat_find_stream_info(mp4_format_cxt,NULL);
    for (int i = 0; i < mp4_format_cxt->nb_streams; ++i) {
        if (mp4_format_cxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            mp4_audio_stream = mp4_format_cxt->streams[i];
            mp4_audio_codec_parameters = mp4_audio_stream->codecpar;
            mp4_audio_codec = avcodec_find_decoder(mp4_audio_codec_parameters->codec_id);
            mp4_audio_codec_cxt = avcodec_alloc_context3(mp4_audio_codec);
            avcodec_parameters_to_context(mp4_audio_codec_cxt,mp4_audio_codec_parameters);
            audio_index = i;
            continue;
        }
        if (mp4_format_cxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
            mp4_video_stream = mp4_format_cxt->streams[i];
            mp4_video_codec_parameters = mp4_video_stream->codecpar;
            mp4_video_codec = avcodec_find_decoder(mp4_video_codec_parameters->codec_id);
            mp4_video_codec_cxt = avcodec_alloc_context3(mp4_video_codec);
            avcodec_parameters_to_context(mp4_video_codec_cxt,mp4_video_codec_parameters);
            video_index = i;
        }
    }
    avcodec_open2(mp4_audio_codec_cxt,mp4_audio_codec,NULL);
    avcodec_open2(mp4_video_codec_cxt,mp4_video_codec,NULL);

    av_dump_format(mp4_format_cxt,0,filename,0);

    avformat_alloc_output_context2(&file_format_cxt,NULL,NULL,"out.mp4");

    int *stream_mapping = NULL;
    int stream_mapping_size = 0;
    int stream_index = 0;

    int stream_audio_index = 0;
    int stream_video_index = 0;
    int stream_data_size = 1000;

    stream_mapping_size = mp4_format_cxt->nb_streams;
    stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));

    file_out_format = file_format_cxt->oformat;

    mp4_format_cxt->nb_streams;

    for (int i = 0; i < mp4_format_cxt->nb_streams; ++i) {
        AVStream *output_stream = NULL;
        AVStream *input_stream = mp4_format_cxt->streams[i];
        AVCodecParameters *input_codec_parameters = input_stream->codecpar;

        if (input_codec_parameters->codec_type != AVMEDIA_TYPE_AUDIO &&
                input_codec_parameters->codec_type != AVMEDIA_TYPE_VIDEO &&
                input_codec_parameters->codec_type != AVMEDIA_TYPE_SUBTITLE){
            stream_mapping[i] = -1;
            continue;
        }

        if(input_codec_parameters->codec_type == AVMEDIA_TYPE_AUDIO){
            stream_audio_index = i;
        }
        if(input_codec_parameters->codec_type == AVMEDIA_TYPE_VIDEO){
            stream_video_index = i;
        }


        stream_mapping[i] = stream_index++;

        output_stream = avformat_new_stream(file_format_cxt,NULL);

        avcodec_parameters_copy(output_stream->codecpar,input_codec_parameters);

        output_stream->codecpar->codec_tag = 0;
    }

    av_dump_format(file_format_cxt,0,"out.mp4",1);

    avio_open(&file_format_cxt->pb,"out.mp4",AVIO_FLAG_WRITE);


    avformat_write_header(file_format_cxt,NULL);

    int64_t  start_time = av_gettime();

    AVPacket *decode_pkt = av_packet_alloc();
    AVFrame *decode_frame = av_frame_alloc();

    AVPacket *encode_pkt = av_packet_alloc();
    AVFrame *encode_frame = av_frame_alloc();

    //------------------------encode start------------------------------
//    encode_audio_codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
    encode_video_codec = avcodec_find_encoder(AV_CODEC_ID_H264);

//    encode_audio_codec_cxt = avcodec_alloc_context3(encode_audio_codec);
    encode_video_codec_cxt = avcodec_alloc_context3(encode_video_codec);

//    encode_audio_codec_cxt->bit_rate = 441000;
//    encode_audio_codec_cxt->time_base =(AVRational){1,441000};
//    encode_audio_codec_cxt->framerate =(AVRational){441000,1};
//    encode_audio_codec_cxt->sample_fmt = AV_SAMPLE_FMT_FLTP;
//    encode_audio_codec_cxt->sample_rate = 44100;
//    encode_audio_codec_cxt->codec_type = AVMEDIA_TYPE_AUDIO;

    encode_video_codec_cxt->codec_type = AVMEDIA_TYPE_VIDEO;
    encode_video_codec_cxt->bit_rate = 400000;
    encode_video_codec_cxt->width = 352;
    encode_video_codec_cxt->height = 288;
    encode_video_codec_cxt->time_base = (AVRational){1,25};
    encode_video_codec_cxt->framerate = (AVRational){25,1};
    encode_video_codec_cxt->gop_size = 250;
    encode_video_codec_cxt->max_b_frames = 3;
    encode_video_codec_cxt->pix_fmt = AV_PIX_FMT_YUV420P;
    encode_video_codec_cxt->me_range = 16;
    encode_video_codec_cxt->max_qdiff = 4;
    encode_video_codec_cxt->qcompress = 0.6;
    encode_video_codec_cxt->qmin = 10;
    encode_video_codec_cxt->qmax = 51;
    av_opt_set(encode_video_codec_cxt->priv_data,"preset","slow",0);

//    ret = avcodec_open2(encode_audio_codec_cxt,encode_audio_codec,NULL);
//    if (ret<0){
//        printf("audio Could not open codec: %s\n", av_err2str(ret));
//    }
    ret = avcodec_open2(encode_video_codec_cxt,encode_video_codec,NULL);
    if (ret<0){
        printf("video Could not open codec: %s\n", av_err2str(ret));
    }

    //------------------------encode end--------------------------------

    //------------------------transcoding start-------------------------

    while(1){
        ret = av_read_frame(mp4_format_cxt,&pkt);
        if (ret < 0 ){
            break;
        }
        if (pkt.stream_index == stream_audio_index){
//            ret = avcodec_send_packet(mp4_audio_codec_cxt,&pkt);
//            while (ret >= 0){
//                ret = avcodec_receive_frame(mp4_audio_codec_cxt,decode_frame);
//                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
//                    break;
//                }
//                printf("audio sample_rate:%d\n",decode_frame->sample_rate);
//
//                ret = avcodec_send_frame(encode_audio_codec_cxt,decode_frame);
//                if(ret <0){
//                    printf("Could not open codec: %s\n", av_err2str(ret));
//                }
//                while(ret >= 0){
//                    ret = avcodec_receive_packet(encode_audio_codec_cxt,encode_pkt);
//                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
//                        break;
//                    }
//                    printf("audio data: %s\n",encode_pkt->data);
//                    av_packet_unref(encode_pkt);
//                }
//            }
        }else if (pkt.stream_index == stream_video_index){

            AVStream *inputStream = mp4_format_cxt->streams[pkt.stream_index];
            pkt.pts = av_rescale_q_rnd(pkt.pts,inputStream->time_base,encode_video_codec_cxt->time_base,AV_ROUND_NEAR_INF);
            pkt.dts = av_rescale_q_rnd(pkt.dts,inputStream->time_base,encode_video_codec_cxt->time_base,AV_ROUND_NEAR_INF);
            pkt.duration = av_rescale_q(pkt.duration,inputStream->time_base,encode_video_codec_cxt->time_base);

            ret = avcodec_send_packet(mp4_video_codec_cxt,&pkt);
            while (ret >= 0){
                ret = avcodec_receive_frame(mp4_video_codec_cxt,decode_frame);
                if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                    break;
                }
                printf("video bit_rate :%lld\n",mp4_video_codec_cxt->bit_rate);

                decode_frame->pts = av_frame_get_best_effort_timestamp(decode_frame);
                encode_pkt->data = NULL;
                encode_pkt->size = 0;
                av_init_packet(encode_pkt);


                ret = avcodec_send_frame(encode_video_codec_cxt,decode_frame);
                if(ret <0){
                    printf("Could not open codec: %s\n", av_err2str(ret));
                }
                while(ret >= 0){
                    ret = avcodec_receive_packet(encode_video_codec_cxt,encode_pkt);
                    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
                        break;
                    }
                    printf("video size: %d\n",encode_pkt->size);
                    ret = av_interleaved_write_frame(file_format_cxt,&pkt);
                }
                av_frame_unref(decode_frame);
//                encode(file_format_cxt,encode_video_codec_cxt,encode_pkt,&pkt,decode_frame);
            }
        }
        av_packet_unref(&pkt);
    }
//    encode(file_format_cxt,encode_video_codec_cxt,encode_pkt,&pkt,NULL);
//    av_write_trailer(file_format_cxt);
    //------------------------transcoding end-------------------------



//    while(1){
//        AVStream *in_stream,*out_stream;
//        ret = av_read_frame(mp4_format_cxt,&pkt);
//        if (ret<0){
//            break;
//        }
//
//        in_stream = mp4_format_cxt->streams[pkt.stream_index];
//        if (pkt.stream_index >= stream_mapping_size || stream_mapping[pkt.stream_index]<0){
//            av_packet_unref(&pkt);
//            continue;
//        }
//
//        pkt.stream_index = stream_mapping[pkt.stream_index];
//        out_stream = file_format_cxt->streams[pkt.stream_index];
//
//        AVRational time_base = out_stream->time_base;
//        AVRational time_base_q = {1,AV_TIME_BASE};
//        int64_t pts_time = av_rescale_q(pkt.dts,time_base,time_base_q);
//        int64_t now_time = av_gettime() - start_time;
//        if (pts_time > now_time){
//            printf("sleep time :%d ms\n",(pts_time - now_time)/1000);
//            av_usleep(pts_time - now_time);
//        }
//
//        pkt.pts = av_rescale_q_rnd(pkt.pts,in_stream->time_base,out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
//        pkt.dts = av_rescale_q_rnd(pkt.dts,in_stream->time_base,out_stream->time_base,AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
//        pkt.duration = av_rescale_q(pkt.duration,in_stream->time_base,out_stream->time_base);
//        pkt.pos;
//
//        ret = av_interleaved_write_frame(file_format_cxt,&pkt);
//        if (ret<0){
//            break;
//        }
//        av_packet_unref(&pkt);
//    }
//    av_write_trailer(file_format_cxt);

    return 0;
}

int muxing_camera_to_file(char *camera_type){
    return 0;
}

int muxing_camera_to_stream(char *camera_type){
    return 0;
}

int muxing_camera_to_screen(){
    //ffmpeg
    AVInputFormat *camera_input_format;
    AVFormatContext *camera_format_cxt = NULL;
    AVStream *camera_video_stream,*camera_audio_stream;
    AVCodecContext *camera_video_codec_cxt = NULL,*camera_audio_codec_cxt = NULL;
    AVCodec *camera_video_codec = NULL,*camera_audio_codec = NULL;
    AVCodecParameters *camera_video_codec_parameters = NULL ,*camera_audio_codec_parameters= NULL;
    AVPacket camera_pkt,*picture_pkt;
    AVFrame *screen_frame,*screen_frame_YUV;
    AVDictionary *dictionary = NULL;
    int video_index = 0;
    int frameFinished = 0;
    int ret = 0;

    //SDL
//    SDL_Surface *screen;
//    SDL_Overlay *bmp;
//    SDL_Rect rect;
    SDL_Texture *bmp = NULL;
    SDL_Window *screen = NULL;
    SDL_Rect rect;
    SDL_Event event;
    SDL_Renderer *renderer = NULL;
    struct SwsContext *sws_cxt = NULL;

    //register
    av_register_all();
    avdevice_register_all();

    //get camera information;
    camera_input_format = av_find_input_format(CAMERA);
    av_dict_set(&dictionary,"framerate","30",0);
    camera_format_cxt = avformat_alloc_context();
    avformat_open_input(&camera_format_cxt,"0:0",camera_input_format,&dictionary);
    //get audio and video streams;
    for (int i = 0; i < camera_format_cxt->nb_streams; ++i) {
        if (camera_format_cxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO){
            camera_video_stream = camera_format_cxt->streams[i];
            camera_video_codec_parameters = camera_video_stream->codecpar;
            camera_video_codec_cxt = avcodec_alloc_context3(NULL);
            avcodec_parameters_to_context(camera_video_codec_cxt,camera_video_codec_parameters);
            video_index = i;
            continue;
        }
        if (camera_format_cxt->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO){
            camera_audio_stream = camera_format_cxt->streams[i];
            camera_audio_codec_parameters = camera_audio_stream->codecpar;
            camera_audio_codec_cxt = avcodec_alloc_context3(NULL);
            avcodec_parameters_to_context(camera_audio_codec_cxt,camera_audio_codec_parameters);
            continue;
        }
    }
    //setting codec context.
    camera_audio_codec =  avcodec_find_decoder(camera_audio_codec_cxt->codec_id);
    camera_video_codec = avcodec_find_decoder(camera_audio_codec_cxt->codec_id);
    //open the codec.
    avcodec_open2(camera_audio_codec_cxt,camera_audio_codec,NULL);
    avcodec_open2(camera_video_codec_cxt,camera_video_codec,NULL);
    //show the information.
    av_dump_format(camera_format_cxt,0,CAMERA,0);
    //frame
    screen_frame = av_frame_alloc();
    screen_frame_YUV = av_frame_alloc();

    printf("%s\n",avcodec_get_name(camera_video_codec_cxt->codec_id));

    printf("%d\n",camera_video_codec_cxt->width);
    printf("%d\n",camera_video_codec_cxt->height);


    AVPacket *pkt = (AVPacket *)av_malloc(sizeof(AVPacket));


    for (;;){
        ret = av_read_frame(camera_format_cxt,pkt);
        if (ret>=0){
            if (pkt->stream_index == video_index){
//                av_parser_parse2()
                while(avcodec_send_packet(camera_video_codec_cxt,pkt)>=0){
                    int i = avcodec_receive_frame(camera_video_codec_cxt,screen_frame);
                    printf("get screen_frame%d\n",i);
                }


            }
        }else{
            break;
        }
    }


    //-------------------------------SDL start-------------------------------.
//    SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER);
//    int screen_width = camera_video_codec_cxt->width,screen_height = camera_video_codec_cxt->height;
//    screen = SDL_CreateWindow("test window",
//                              SDL_WINDOWPOS_UNDEFINED,
//                              SDL_WINDOWPOS_UNDEFINED,
//                              camera_video_codec_cxt->width,
//                              camera_video_codec_cxt->height,
//                              SDL_WINDOW_FULLSCREEN|SDL_WINDOW_OPENGL);
//    renderer = SDL_CreateRenderer(screen,-1,0);
//
//    bmp = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YV12,SDL_TEXTUREACCESS_STREAMING,camera_video_codec_cxt->width,camera_video_codec_cxt->height);
//
//    sws_cxt = sws_getContext(camera_video_codec_cxt->width,
//                             camera_video_codec_cxt->height,
//                             camera_video_codec_cxt->pix_fmt,
//                             camera_video_codec_cxt->width,
//                             camera_video_codec_cxt->height,
//                             AV_PIX_FMT_YUV420P,
//                             SWS_BILINEAR,
//                             NULL,NULL,NULL);
//    int numBytes = avpicture_get_size(AV_PIX_FMT_YUV420P,camera_video_codec_cxt->width,camera_video_codec_cxt->height);
//    uint8_t buffer = (uint8_t *)av_malloc(numBytes* sizeof(uint8_t));
//    avpicture_fill((AVPicture *)screen_frame_YUV,buffer,AV_PIX_FMT_YUV420P,camera_video_codec_cxt->width,camera_video_codec_cxt->height);
//    video_index = 0;
//    rect.x = 0;
//    rect.y = 0;
//    rect.w = camera_video_codec_cxt->width;
//    rect.h = camera_video_codec_cxt->height;
//
//    while(av_read_frame(camera_format_cxt,&camera_pkt)>=0){
//        if(picture_pkt->stream_index == video_index){
//            avcodec_decode_video2(camera_video_codec_cxt,screen_frame,&frameFinished,&camera_pkt);
//            if (frameFinished){
//                sws_scale(sws_cxt,
//                          (uint8_t const* const*)screen_frame->data,
//                          screen_frame->linesize,
//                          0,
//                          camera_video_codec_cxt->height,
//                          screen_frame_YUV->data,
//                          screen_frame_YUV->linesize);
//                SDL_UpdateTexture(bmp,&camera_pkt,screen_frame_YUV->data[0],screen_frame_YUV->linesize[0]);
//                SDL_RenderClear(renderer);
//                SDL_RenderCopy(renderer,bmp,&rect,&rect);
//                SDL_RenderPresent(renderer);
//            }
//            SDL_Delay(50);
//        }
//
//        av_packet_unref(&camera_pkt);
//        SDL_PollEvent(&event);
//        switch(event.type){
//            case SDL_QUIT:
//                SDL_Quit();
//                exit(0);
//                break;
//            default:
//                break;
//        }
//    }
//    SDL_DestroyTexture(bmp);
//    av_free(screen_frame);
//    av_free(screen_frame_YUV);
//    avformat_close_input(&camera_format_cxt);


    return 0;
}

void decode(AVCodecContext *av_codec_context,AVPacket *av_packet,AVFrame *av_frame) {
    int ret = 0;
    ret = avcodec_send_packet(av_codec_context, av_packet);
    while (ret >= 0) {
        ret = avcodec_receive_frame(av_codec_context, av_frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
            break;
        }
    }
}
//
//void encode(AVFormatContext *av_format_context,AVCodecContext *av_codec_context,AVPacket *av_packet,AVPacket *pkt,AVFrame *av_frame){
//    int ret = 0;
//    ret = avcodec_send_frame(av_codec_context,av_frame);
//    if(ret <0){
//        printf("Could not open codec: %s\n", av_err2str(ret));
//    }
//    while(ret >= 0){
//        ret = avcodec_receive_packet(av_codec_context,av_packet);
//        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
//            break;
//        }
//        printf("video data: %s\n",av_packet->data);
//        ret = av_interleaved_write_frame(av_format_context,av_packet);
//    }
//    av_packet_unref(av_packet);
//    av_packet_unref(pkt);
//}
